🔌 refactor: Decouple bash_tool from Per-User CODE_API_KEY#12712
🔌 refactor: Decouple bash_tool from Per-User CODE_API_KEY#12712danny-avila merged 4 commits intofeat/agent-skillsfrom
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 50bb359682
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const codeApiKey = process.env[EnvVar.CODE_API_KEY] ?? ''; | ||
| if (codeApiKey) { |
There was a problem hiding this comment.
Enforce execute_code capability before priming skill files
This path now primes skill files whenever LIBRECHAT_CODE_API_KEY is present in the server environment, but it no longer checks whether code execution is enabled for the run/agent. In deployments where skills are enabled but execute_code is intentionally disabled, invoking a file-backed skill will still upload files to the code sandbox, causing unintended data egress and background code-env activity. Please gate this branch on the same execute-code capability signal used when registering bash_tool.
Useful? React with 👍 / 👎.
| ); | ||
| } | ||
| } | ||
| const apiKey = process.env[EnvVar.CODE_API_KEY] ?? ''; |
There was a problem hiding this comment.
Skip history file re-priming when execute_code is disabled
primeInvokedSkills now derives the API key directly from process env and will re-prime historical skill files whenever that env var is set, even if the current run does not have code execution enabled. That means conversations can trigger sandbox uploads during history reconstruction despite bash_tool being unavailable for the agent, which is both unnecessary and a data-scope regression. Add an execute-code capability gate before entering the priming/upload path.
Useful? React with 👍 / 👎.
Addresses review feedback on #12712: the previous commit gated skill file priming only on process.env[EnvVar.CODE_API_KEY] presence, which meant agents with execute_code disabled but the env var present would still upload bundled skill files to the sandbox on every invocation — a data-scope regression. Thread the execute_code capability flag (codeEnvAvailable) through enrichWithSkillConfigurable → configurable, and as a dep on primeInvokedSkills. Both handleSkillToolCall and primeInvokedSkills gate the sandbox-upload path on codeEnvAvailable first; the API key is then read from env inside that gate (if also missing, skip with a warn — admin misconfig). Adds skillFiles.spec.ts pinning the four-way matrix: capability on/off × env key set/unset.
75029e9 to
d519c82
Compare
Addresses review feedback on #12712: the previous commit gated skill file priming only on process.env[EnvVar.CODE_API_KEY] presence, which meant agents with execute_code disabled but the env var present would still upload bundled skill files to the sandbox on every invocation — a data-scope regression. Thread the execute_code capability flag (codeEnvAvailable) through enrichWithSkillConfigurable → configurable, and as a dep on primeInvokedSkills. Both handleSkillToolCall and primeInvokedSkills gate the sandbox-upload path on codeEnvAvailable first; the API key is then read from env inside that gate (if also missing, skip with a warn — admin misconfig). Adds skillFiles.spec.ts pinning the four-way matrix: capability on/off × env key set/unset.
be95291 to
c7c73a2
Compare
4ac1353 to
d08e784
Compare
c7c73a2 to
f30038b
Compare
|
@codex review |
|
Codex Review: Didn't find any major issues. Swish! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f30038bcc1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const isBashTool = toolNames.includes(AgentConstants.BASH_TOOL); | ||
| if (isBashTool) { | ||
| try { | ||
| const authValues = await loadAuthValues({ | ||
| userId: req.user.id, | ||
| authFields: [EnvVar.CODE_API_KEY], | ||
| }); | ||
| const codeApiKey = authValues[EnvVar.CODE_API_KEY]; | ||
|
|
||
| if (codeApiKey) { | ||
| const bashTool = createBashExecutionTool({ apiKey: codeApiKey }); | ||
| allLoadedTools.push(bashTool); | ||
| } else { | ||
| logger.debug('[loadToolsForExecution] bash_tool requested but CODE_API_KEY not available'); | ||
| allLoadedTools.push( | ||
| toolFn( | ||
| async () => [ | ||
| 'Code execution is not available. Use the read_file tool instead.', | ||
| undefined, | ||
| ], | ||
| { | ||
| name: AgentConstants.BASH_TOOL, | ||
| description: 'Bash execution (unavailable - no code API key configured)', | ||
| schema: { type: 'object', properties: { command: { type: 'string' } } }, | ||
| responseFormat: AgentConstants.CONTENT_AND_ARTIFACT, | ||
| }, | ||
| ), | ||
| ); | ||
| } | ||
| const bashTool = createBashExecutionTool({}); | ||
| allLoadedTools.push(bashTool); |
There was a problem hiding this comment.
Gate bash_tool loading on execute_code capability
This branch now creates bash_tool solely from the requested tool name and no longer enforces the execute_code capability before loading it. Because ON_TOOL_EXECUTE loads tools from model-emitted tool-call names, a stray/hallucinated bash_tool call can still be executed when execute_code is disabled for that agent/tenant if LIBRECHAT_CODE_API_KEY exists in server env. Add an explicit capability/allowlist check before constructing and registering bash_tool.
Useful? React with 👍 / 👎.
Resolves findings from the second codex review on #12712: - MAJOR: `handlers.spec.ts` now covers the `codeEnvAvailable` gate in `handleSkillToolCall` across three cases (gate off, gate on + env set, gate on + env unset). The gate is the critical regression prevention — a future edit that drops it would silently re-enable sandbox uploads for agents with `execute_code` disabled. - MINOR: Hoist `codeEnvAvailable` and `skillPrimedIdsByName` out of `loadTools` closures in `openai.js` and `responses.js`. Both values are fixed once `initializeAgent` resolves, so recomputing them on every tool execution was wasted work. `responses.js` shares a single pair between its streaming and non-streaming branches. - MINOR: `skillFiles.spec.ts` now has a test that exercises the full upload path end-to-end with real file records, asserting `batchUploadCodeEnvFiles` is called with the env-sourced apiKey and the correct file set (including the synthetic `SKILL.md`). - NIT: Finish the `appConfig` extraction in `responses.js/createResponse` — replaces the remaining `req.config` references with `appConfig` for consistency with the pattern in other controllers. No behavioral changes beyond what was already in place; this is coverage and readability polish.
|
Audited the review — disposition: Fixed in a16c612:
Rejected with rationale:
CI still green. |
|
@codex review |
|
Codex Review: Didn't find any major issues. 👍 ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
Follow-up review audited — both NITs addressed in 6e69643:
On the flagged CI |
Phase 4 of Agent Skills umbrella (#12625): gate bash_tool and skill file priming on the `execute_code` capability only. Thread a boolean `codeEnvAvailable` through `enrichWithSkillConfigurable` and `primeInvokedSkills` in place of the old per-user `codeApiKey` + `loadAuthValues` plumbing. The sandbox API key is the LibreChat- hosted service key — system-level, not a user secret — so the per-user lookup was legacy; when needed, it's read directly from `process.env[EnvVar.CODE_API_KEY]` inside the capability gate. `handleSkillToolCall` and `primeInvokedSkills` gate sandbox uploads on `codeEnvAvailable` first, preventing skill-file uploads to the sandbox when an agent has `execute_code` disabled even if the env var happens to be set. The agents library resolves the env key itself for `bash_tool`, so `ToolService.js` drops the `loadAuthValues` lookup and the "Code execution is not available" placeholder tool in favor of a plain `createBashExecutionTool({})` with a loud error log if the env var is missing. Also fixes a pre-existing `appConfig`-undefined lint error in `responses.js`/`createResponse` that surfaced when this file was touched (declares `const appConfig = req.config` at function top, matching the existing pattern in other controllers). Preserves the `skillPrimedIdsByName` threading added by Phase 3/5/6 and all Phase 3/5/6 call-site signatures. Adds `skillConfigurable.spec.ts` (5 cases pinning the new surface) and `skillFiles.spec.ts` (4-way matrix of capability × env key for `primeInvokedSkills`).
Resolves findings from the second codex review on #12712: - MAJOR: `handlers.spec.ts` now covers the `codeEnvAvailable` gate in `handleSkillToolCall` across three cases (gate off, gate on + env set, gate on + env unset). The gate is the critical regression prevention — a future edit that drops it would silently re-enable sandbox uploads for agents with `execute_code` disabled. - MINOR: Hoist `codeEnvAvailable` and `skillPrimedIdsByName` out of `loadTools` closures in `openai.js` and `responses.js`. Both values are fixed once `initializeAgent` resolves, so recomputing them on every tool execution was wasted work. `responses.js` shares a single pair between its streaming and non-streaming branches. - MINOR: `skillFiles.spec.ts` now has a test that exercises the full upload path end-to-end with real file records, asserting `batchUploadCodeEnvFiles` is called with the env-sourced apiKey and the correct file set (including the synthetic `SKILL.md`). - NIT: Finish the `appConfig` extraction in `responses.js/createResponse` — replaces the remaining `req.config` references with `appConfig` for consistency with the pattern in other controllers. No behavioral changes beyond what was already in place; this is coverage and readability polish.
Round-3 codex review flagged two NITs on the test code added in the
previous commit:
- Replace `_id: 'skill-id' as unknown as never` in the new
`makeSkillHandlerWithFiles` helper with a real `Types.ObjectId`,
matching the pattern used by the primed-skill tests further up in
the same file (and by `skillFiles.spec.ts`). The `never` cast
hides the fact that `_id` really is a string / ObjectId at runtime.
- Replace the ad-hoc `{ on, pipe, read }` stub with a real
`Readable.from(Buffer.from(''))` in the upload-path test. The stub
worked only because `batchUploadCodeEnvFiles` is mocked and never
iterates the stream; `Readable.from` satisfies the same contract
and is robust to any future partial-real replacement of the upload
function.
Pure test-hygiene improvements; no runtime code touched.
The upstream `2463b6acd` fix declared `const appConfig = req.config` inside the try block (line 381) to patch the same `no-undef` error I fixed in this PR at the top of `createResponse` (line 283). The rebase kept both declarations side-by-side. Drops the inner one — the outer binding covers every downstream reference already.
6e69643 to
66037f3
Compare
* 🔌 refactor: Decouple bash_tool from Per-User CODE_API_KEY Phase 4 of Agent Skills umbrella (#12625): gate bash_tool and skill file priming on the `execute_code` capability only. Thread a boolean `codeEnvAvailable` through `enrichWithSkillConfigurable` and `primeInvokedSkills` in place of the old per-user `codeApiKey` + `loadAuthValues` plumbing. The sandbox API key is the LibreChat- hosted service key — system-level, not a user secret — so the per-user lookup was legacy; when needed, it's read directly from `process.env[EnvVar.CODE_API_KEY]` inside the capability gate. `handleSkillToolCall` and `primeInvokedSkills` gate sandbox uploads on `codeEnvAvailable` first, preventing skill-file uploads to the sandbox when an agent has `execute_code` disabled even if the env var happens to be set. The agents library resolves the env key itself for `bash_tool`, so `ToolService.js` drops the `loadAuthValues` lookup and the "Code execution is not available" placeholder tool in favor of a plain `createBashExecutionTool({})` with a loud error log if the env var is missing. Also fixes a pre-existing `appConfig`-undefined lint error in `responses.js`/`createResponse` that surfaced when this file was touched (declares `const appConfig = req.config` at function top, matching the existing pattern in other controllers). Preserves the `skillPrimedIdsByName` threading added by Phase 3/5/6 and all Phase 3/5/6 call-site signatures. Adds `skillConfigurable.spec.ts` (5 cases pinning the new surface) and `skillFiles.spec.ts` (4-way matrix of capability × env key for `primeInvokedSkills`). * 🧪 refactor: Address Codex Review Feedback Resolves findings from the second codex review on #12712: - MAJOR: `handlers.spec.ts` now covers the `codeEnvAvailable` gate in `handleSkillToolCall` across three cases (gate off, gate on + env set, gate on + env unset). The gate is the critical regression prevention — a future edit that drops it would silently re-enable sandbox uploads for agents with `execute_code` disabled. - MINOR: Hoist `codeEnvAvailable` and `skillPrimedIdsByName` out of `loadTools` closures in `openai.js` and `responses.js`. Both values are fixed once `initializeAgent` resolves, so recomputing them on every tool execution was wasted work. `responses.js` shares a single pair between its streaming and non-streaming branches. - MINOR: `skillFiles.spec.ts` now has a test that exercises the full upload path end-to-end with real file records, asserting `batchUploadCodeEnvFiles` is called with the env-sourced apiKey and the correct file set (including the synthetic `SKILL.md`). - NIT: Finish the `appConfig` extraction in `responses.js/createResponse` — replaces the remaining `req.config` references with `appConfig` for consistency with the pattern in other controllers. No behavioral changes beyond what was already in place; this is coverage and readability polish. * 🧷 test: Tighten Spec Hygiene Per Codex Nit Feedback Round-3 codex review flagged two NITs on the test code added in the previous commit: - Replace `_id: 'skill-id' as unknown as never` in the new `makeSkillHandlerWithFiles` helper with a real `Types.ObjectId`, matching the pattern used by the primed-skill tests further up in the same file (and by `skillFiles.spec.ts`). The `never` cast hides the fact that `_id` really is a string / ObjectId at runtime. - Replace the ad-hoc `{ on, pipe, read }` stub with a real `Readable.from(Buffer.from(''))` in the upload-path test. The stub worked only because `batchUploadCodeEnvFiles` is mocked and never iterates the stream; `Readable.from` satisfies the same contract and is robust to any future partial-real replacement of the upload function. Pure test-hygiene improvements; no runtime code touched. * 🧹 chore: Remove Duplicate appConfig Declaration After Rebase The upstream `2463b6acd` fix declared `const appConfig = req.config` inside the try block (line 381) to patch the same `no-undef` error I fixed in this PR at the top of `createResponse` (line 283). The rebase kept both declarations side-by-side. Drops the inner one — the outer binding covers every downstream reference already.
* 🔌 refactor: Decouple bash_tool from Per-User CODE_API_KEY Phase 4 of Agent Skills umbrella (#12625): gate bash_tool and skill file priming on the `execute_code` capability only. Thread a boolean `codeEnvAvailable` through `enrichWithSkillConfigurable` and `primeInvokedSkills` in place of the old per-user `codeApiKey` + `loadAuthValues` plumbing. The sandbox API key is the LibreChat- hosted service key — system-level, not a user secret — so the per-user lookup was legacy; when needed, it's read directly from `process.env[EnvVar.CODE_API_KEY]` inside the capability gate. `handleSkillToolCall` and `primeInvokedSkills` gate sandbox uploads on `codeEnvAvailable` first, preventing skill-file uploads to the sandbox when an agent has `execute_code` disabled even if the env var happens to be set. The agents library resolves the env key itself for `bash_tool`, so `ToolService.js` drops the `loadAuthValues` lookup and the "Code execution is not available" placeholder tool in favor of a plain `createBashExecutionTool({})` with a loud error log if the env var is missing. Also fixes a pre-existing `appConfig`-undefined lint error in `responses.js`/`createResponse` that surfaced when this file was touched (declares `const appConfig = req.config` at function top, matching the existing pattern in other controllers). Preserves the `skillPrimedIdsByName` threading added by Phase 3/5/6 and all Phase 3/5/6 call-site signatures. Adds `skillConfigurable.spec.ts` (5 cases pinning the new surface) and `skillFiles.spec.ts` (4-way matrix of capability × env key for `primeInvokedSkills`). * 🧪 refactor: Address Codex Review Feedback Resolves findings from the second codex review on #12712: - MAJOR: `handlers.spec.ts` now covers the `codeEnvAvailable` gate in `handleSkillToolCall` across three cases (gate off, gate on + env set, gate on + env unset). The gate is the critical regression prevention — a future edit that drops it would silently re-enable sandbox uploads for agents with `execute_code` disabled. - MINOR: Hoist `codeEnvAvailable` and `skillPrimedIdsByName` out of `loadTools` closures in `openai.js` and `responses.js`. Both values are fixed once `initializeAgent` resolves, so recomputing them on every tool execution was wasted work. `responses.js` shares a single pair between its streaming and non-streaming branches. - MINOR: `skillFiles.spec.ts` now has a test that exercises the full upload path end-to-end with real file records, asserting `batchUploadCodeEnvFiles` is called with the env-sourced apiKey and the correct file set (including the synthetic `SKILL.md`). - NIT: Finish the `appConfig` extraction in `responses.js/createResponse` — replaces the remaining `req.config` references with `appConfig` for consistency with the pattern in other controllers. No behavioral changes beyond what was already in place; this is coverage and readability polish. * 🧷 test: Tighten Spec Hygiene Per Codex Nit Feedback Round-3 codex review flagged two NITs on the test code added in the previous commit: - Replace `_id: 'skill-id' as unknown as never` in the new `makeSkillHandlerWithFiles` helper with a real `Types.ObjectId`, matching the pattern used by the primed-skill tests further up in the same file (and by `skillFiles.spec.ts`). The `never` cast hides the fact that `_id` really is a string / ObjectId at runtime. - Replace the ad-hoc `{ on, pipe, read }` stub with a real `Readable.from(Buffer.from(''))` in the upload-path test. The stub worked only because `batchUploadCodeEnvFiles` is mocked and never iterates the stream; `Readable.from` satisfies the same contract and is robust to any future partial-real replacement of the upload function. Pure test-hygiene improvements; no runtime code touched. * 🧹 chore: Remove Duplicate appConfig Declaration After Rebase The upstream `2463b6acd` fix declared `const appConfig = req.config` inside the try block (line 381) to patch the same `no-undef` error I fixed in this PR at the top of `createResponse` (line 283). The rebase kept both declarations side-by-side. Drops the inner one — the outer binding covers every downstream reference already.
* 🔌 refactor: Decouple bash_tool from Per-User CODE_API_KEY Phase 4 of Agent Skills umbrella (#12625): gate bash_tool and skill file priming on the `execute_code` capability only. Thread a boolean `codeEnvAvailable` through `enrichWithSkillConfigurable` and `primeInvokedSkills` in place of the old per-user `codeApiKey` + `loadAuthValues` plumbing. The sandbox API key is the LibreChat- hosted service key — system-level, not a user secret — so the per-user lookup was legacy; when needed, it's read directly from `process.env[EnvVar.CODE_API_KEY]` inside the capability gate. `handleSkillToolCall` and `primeInvokedSkills` gate sandbox uploads on `codeEnvAvailable` first, preventing skill-file uploads to the sandbox when an agent has `execute_code` disabled even if the env var happens to be set. The agents library resolves the env key itself for `bash_tool`, so `ToolService.js` drops the `loadAuthValues` lookup and the "Code execution is not available" placeholder tool in favor of a plain `createBashExecutionTool({})` with a loud error log if the env var is missing. Also fixes a pre-existing `appConfig`-undefined lint error in `responses.js`/`createResponse` that surfaced when this file was touched (declares `const appConfig = req.config` at function top, matching the existing pattern in other controllers). Preserves the `skillPrimedIdsByName` threading added by Phase 3/5/6 and all Phase 3/5/6 call-site signatures. Adds `skillConfigurable.spec.ts` (5 cases pinning the new surface) and `skillFiles.spec.ts` (4-way matrix of capability × env key for `primeInvokedSkills`). * 🧪 refactor: Address Codex Review Feedback Resolves findings from the second codex review on #12712: - MAJOR: `handlers.spec.ts` now covers the `codeEnvAvailable` gate in `handleSkillToolCall` across three cases (gate off, gate on + env set, gate on + env unset). The gate is the critical regression prevention — a future edit that drops it would silently re-enable sandbox uploads for agents with `execute_code` disabled. - MINOR: Hoist `codeEnvAvailable` and `skillPrimedIdsByName` out of `loadTools` closures in `openai.js` and `responses.js`. Both values are fixed once `initializeAgent` resolves, so recomputing them on every tool execution was wasted work. `responses.js` shares a single pair between its streaming and non-streaming branches. - MINOR: `skillFiles.spec.ts` now has a test that exercises the full upload path end-to-end with real file records, asserting `batchUploadCodeEnvFiles` is called with the env-sourced apiKey and the correct file set (including the synthetic `SKILL.md`). - NIT: Finish the `appConfig` extraction in `responses.js/createResponse` — replaces the remaining `req.config` references with `appConfig` for consistency with the pattern in other controllers. No behavioral changes beyond what was already in place; this is coverage and readability polish. * 🧷 test: Tighten Spec Hygiene Per Codex Nit Feedback Round-3 codex review flagged two NITs on the test code added in the previous commit: - Replace `_id: 'skill-id' as unknown as never` in the new `makeSkillHandlerWithFiles` helper with a real `Types.ObjectId`, matching the pattern used by the primed-skill tests further up in the same file (and by `skillFiles.spec.ts`). The `never` cast hides the fact that `_id` really is a string / ObjectId at runtime. - Replace the ad-hoc `{ on, pipe, read }` stub with a real `Readable.from(Buffer.from(''))` in the upload-path test. The stub worked only because `batchUploadCodeEnvFiles` is mocked and never iterates the stream; `Readable.from` satisfies the same contract and is robust to any future partial-real replacement of the upload function. Pure test-hygiene improvements; no runtime code touched. * 🧹 chore: Remove Duplicate appConfig Declaration After Rebase The upstream `2463b6acd` fix declared `const appConfig = req.config` inside the try block (line 381) to patch the same `no-undef` error I fixed in this PR at the top of `createResponse` (line 283). The rebase kept both declarations side-by-side. Drops the inner one — the outer binding covers every downstream reference already.
Summary
I decoupled
bash_toolregistration and skill-file priming from per-userCODE_API_KEYlookups as part of Phase 4 of the Agent Skills umbrella (#12625).LIBRECHAT_CODE_API_KEYis the LibreChat-hosted sandbox service key — a system-level credential, not a per-user secret — so sandbox-touching paths are now gated solely on theexecute_codecapability, and the key itself is read fromprocess.envinside that gate.codeEnvAvailablethroughenrichWithSkillConfigurableandprimeInvokedSkillsin place of the oldcodeApiKey+loadAuthValuesplumbing.handleSkillToolCallfile priming oncodeEnvAvailablefrommergedConfigurableso an agent withexecute_codedisabled cannot trigger sandbox uploads even when the env var is present.bash_toolloader inToolService.jsto callcreateBashExecutionTool({})— the agents library readsLIBRECHAT_CODE_API_KEYfrom the environment internally. Removed the "Code execution is not available" placeholder tool that lied to the model in its tool list when the env var was missing.appConfig-undefined lint error inresponses.js/createResponsethat surfaced when this file was touched, and finished the extraction so allreq.configreferences in that function now go throughappConfig.codeEnvAvailableandskillPrimedIdsByNameout of theloadToolsclosures inopenai.jsandresponses.js; both values are fixed onceinitializeAgentresolves, so recomputing them per tool execution was wasted work.packages/api/src/agents/skillConfigurable.spec.ts(5 cases pinning the new surface contract).packages/api/src/agents/skillFiles.spec.ts(5 cases covering the 4-way capability × env-key matrix plus an end-to-end upload assertion).packages/api/src/agents/handlers.spec.tscovering thecodeEnvAvailablegate inhandleSkillToolCall.Out of scope (intentionally untouched): the legacy
CodeExecutionToolpath inhandleTools.js, post-execution file-download callbacks, Programmatic Tool Calling registration, and the per-userexecute_codeplugin install hook in the frontend (follow-up tracked in #12713).Change Type
Testing
Ran from
packages/api:npx jest src/agents/skillConfigurable.spec.ts— 5/5 pass.npx jest src/agents/skillFiles.spec.ts— 5/5 pass.npx jest src/agents/handlers.spec.ts— 20/20 pass (3 new capability-gate cases).npx jest src/agents/__tests__/skills.test.ts— 36/36 pass.npx jest src/agents— 591+ pass; the only failures (2) are a pre-existing ESM dynamic-import issue insummarization.e2e.test.ts, unchanged by this PR.Ran from
api:npx jest server/services/__tests__/ToolService.spec.js— 42/42 pass.Recommended manual verification in a full install:
LIBRECHAT_CODE_API_KEYset andexecute_codeenabled on an agent with a skill → skill invocation runs bash commands and primes files.execute_codedisabled →bash_toolis absent from definitions and skill file priming is skipped (unchanged).Test Configuration:
feat/agent-skillsChecklist